[Day15] 再厲害的能力者一碰到海樓石就會喪失戰鬥力!
今天要和大家介紹 Ruby 裡的存取控制(Access control)
先來看看維基百科怎麼說:
存取控制
是指允許或禁止某人使用某項資源的能力。
聽起來是不是超強!
不過在 Ruby 裡,存取控制其實就是方法的使用層級。當我們在類別裡定義方法時,還可以再更明確指出這個方法是只有誰才可以取用,共分為三種:
如果沒有特別寫是哪種存取方式,就會預設用 public
來存取我們定義的方法。
在撰寫存取控制的層級時,與方法定義前綴詞同一行即可:
class Cafe
public # 通常直接省略不寫
def coffee
puts "來杯西西里拿坡里左岸八里漂浮拿鐵!"
end
end
換句話說,平常不寫 public
也沒關係,除非你在定義了 private
方法後又要定義 public
方法才要特別加上去,不過通常我們是不會這樣做的,後面會提到原因。
private
則是指所定義的方法不能有明確的訊息接收者(receiver),意思就是不能用.
來呼叫方法
像是 puts
就是一種很常見的 private
方法,不過我們自己定義一個 private 方法 seasoning
試試看:
class Cafe
def coffee
puts "來杯西西里拿坡里左岸八里漂浮拿鐵!"
end
private
def seansoning
puts "還要有絕妙的香氣!"
end
end
my_store = Cafe.new
my_store.coffee
my_store.seasoning
# 印出
來杯西西里拿坡里左岸八里漂浮拿鐵!
NoMethodError (undefined method `seasoning' for #<Cafe:0x00007f903c945538>)
我開的咖啡廳裡面沒有絕妙的香氣!
看來 private
的方法沒辦法像之前的方式直接呼叫,那它到底是用在哪裡呢?事實上,Ruby 裡用 private
寫的方法是拿來在類別內部呼叫的,像是這樣:
class Cafe
def coffee
puts "來杯西西里拿坡里左岸八里漂浮拿鐵!"
seasoning
end
private
def seasoning
puts "還要有絕妙的香氣!"
end
end
my_store = Cafe.new
my_store.coffee
# 印出
來杯西西里拿坡里左岸八里漂浮拿鐵!
還要有絕妙的香氣!
在 Ruby 裡,private
寫的方法不只在類別內部可以存取,繼承自這個類別的子類別也一樣可以!
class Father
private
def heritage(gift)
puts "#{gift} 是我留給兒子的財產!"
end
end
class Son < Father
def initialize(item)
heritage(item)
puts "謝謝老爸!"
end
end
Son.new("PS5")
# 印出
PS5 是我留給兒子的財產!
謝謝老爸!
我也想要有個這樣的老爸(羨慕)
可以用 self.class.private_methods.sort
來找 :
self.class.private_methods.sort
=> [:Array, :Complex, :DelegateClass, :Float, :Hash, :Integer, :Rational, :String, :URI, :__callee__, :__dir__, :__method__, :`, :abort, :at_exit, :binding, :block_given?, :caller, :caller_locations, :catch, :eval, :exec, :exit, :exit!, :extended, :fail, :fork, :format, :gem, :gem_original_require, :gets, :global_variables, :included, :inherited, :initialize, :initialize_clone, :initialize_copy, :initialize_dup, :irb_binding, :iterator?, :lambda, :load, :local_variables, :loop, :method_added, :method_missing, :method_removed, :method_undefined, :open, :p, :prepended, :print, :printf, :private, :proc, :protected, :public, :putc, :puts, :raise, :rand, :readline, :readlines, :remove_const, :require, :require_relative, :respond_to_missing?, :select, :set_trace_func, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :sleep, :spawn, :sprintf, :srand, :syscall, :system, :test, :throw, :trace_var, :trap, :untrace_var, :using, :warn]
發現了嗎? private
和 public
都是 private 方法!
那... private
裡面的方法是不是就再也無法拿出來用呢?其實也未必。我們可以用 .send()
來取出 private
方法,只是這樣會破壞封裝的原則,非不得已建議不要這樣做。
class Man
private
def whisper
puts "這是只有 private 才聽得到的悄悄話..."
end
end
man = Man.new
man.send(:whisper)
# 印出
這是只有 private 才聽得到的悄悄話...
果然成功拿出來了!
這是一個既不 public 又不 private 的東西。
protected
和 private
最大的不同,是 protected
可以允許在呼叫時前面有 receiver
,這種承繼自 SmallTalk 的設計理念是 Ruby 的一大特點,不過可能之後才會詳細介紹這段。
但由於實際上現在幾乎沒有人在用 protected
寫 Ruby 的存取控制,就讓它慢慢地成為時代的眼淚吧!
class Smalltalk
def say_hello_to_myself
self.hello
end
protected
def hello
puts "murmur..."
end
end
如果是用 protected
,那麼在內部呼叫時要寫 self.hello
或hello
都可以,但改成寫 private
,self.hello
就會出錯了。
也可以這樣寫:
class Gem
def ruby
end
def emerald
end
def diamond
end
protected :emerald
private :diamond
end
這麽一來,emerald
就變成了 protected
方法,而 diamond
成為了 private
方法
public
、protected
和 private
的特點有三:
public
方法能在外部被呼叫。private
的方法不允許有 receiver
, protected
則沒有這種限制。不過用 send()
可以取消 private
原本的限制。在實際撰寫時,我們會把 private
的方法定義在類別的最下方,這樣就能知道 private
上方的通通都是 public
的方法,也能更輕鬆地閱讀程式碼。
關於 Ruby 方法的存取控制,今天就介紹到這邊啦!
參考來源:
高見龍 - Public, Protected and Private Method in Ruby